package com.ibeeking.found.upms.cp.rest.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ibeeking.found.common.base.BaseDO;
import com.ibeeking.found.common.config.feign.GlobalClient;
import com.ibeeking.found.common.config.feign.MsgFeignClient;
import com.ibeeking.found.common.config.prop.FileOssProp;
import com.ibeeking.found.common.config.secret.RsaConfig;
import com.ibeeking.found.common.constants.DefaultPathConstant;
import com.ibeeking.found.common.constants.HeaderNounConstant;
import com.ibeeking.found.common.entity.CurrentUser;
import com.ibeeking.found.common.entity.LoginUserDTO;
import com.ibeeking.found.common.entity.TenantDTO;
import com.ibeeking.found.common.entity.UserInfoDTO;
import com.ibeeking.found.common.enums.LoginRoleEnum;
import com.ibeeking.found.common.enums.PublishStatusEnum;
import com.ibeeking.found.common.enums.SexEnum;
import com.ibeeking.found.common.enums.member.MemberSexEnum;
import com.ibeeking.found.common.utils.UrlUtils;
import com.ibeeking.found.common.utils.UserUtils;
import com.ibeeking.found.id.GenerateId;
import com.ibeeking.found.upms.api.common.dto.ProjectDTO;
import com.ibeeking.found.upms.api.common.query.AccountLoginQuery;
import com.ibeeking.found.upms.api.feign.SystemProjectClient;
import com.ibeeking.found.upms.cp.rest.config.prop.UserConfigProp;
import com.ibeeking.found.upms.cp.rest.service.ICpUserService;
import com.ibeeking.found.upms.service.common.bo.TenantUserBO;
import com.ibeeking.found.upms.service.common.dos.ShopUserDO;
import com.ibeeking.found.upms.service.common.dos.TenantUserDO;
import com.ibeeking.found.upms.service.common.param.TenantUserParam;
import com.ibeeking.found.upms.service.common.param.ModifyMobileParam;
import com.ibeeking.found.upms.service.common.param.UserFindPwdParam;
import com.ibeeking.found.upms.service.common.param.UserModifyPwdParam;
import com.ibeeking.found.upms.service.common.po.TenantUserPO;
import com.ibeeking.found.upms.service.common.query.TenantUserPageQuery;
import com.ibeeking.found.upms.service.common.vo.CurrentCpUserVO;
import com.ibeeking.found.upms.service.common.vo.TenantUserVO;
import com.ibeeking.found.upms.service.mapper.CpUserMapper;
import com.ibeeking.found.upms.service.mapper.ShopUserMapper;
import com.ibeeking.found.upms.service.mapper.TenantUserMapper;
import com.ibeeking.nematos.core.utils.SpringBeanUtil;
import com.ibeeking.nematos.exception.BusinessException;
import com.ibeeking.nematos.exception.EncryptException;

import com.ibeeking.nematos.utils.bean.BeanUtil;
import com.ibeeking.nematos.utils.data.Collections3;
import com.ibeeking.nematos.utils.data.StringUtils;
import com.ibeeking.nematos.utils.encryption.EncryptUtils;
import com.ibeeking.nematos.utils.json.JsonUtils;
import com.ibeeking.nematos.utils.result.ResponseResult;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @ClassName CpUserServiceImpl
 * @Description 全局用户
 * @Author ibeeking
 * @Date 2020-12-01 18:36
 **/
@Service
public class CpUserServiceImpl implements ICpUserService {

    @Resource
    private TenantUserMapper tenantUserMapper;
    @Resource
    private ShopUserMapper shopUserMapper;
    @Resource
    private CpUserMapper cpUserMapper;
    @Resource
    private FileOssProp fileOssProp;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private MsgFeignClient msgClient;
    @Resource
    private UserConfigProp userConfigProp;
    @Resource
    private RsaConfig rsaConfig;
    @Resource
    private GlobalClient globalClient;
    @Resource
    private SystemProjectClient systemProjectClient;

    @Override
    public Page<TenantUserVO> pageList(TenantUserPageQuery query) {
        LambdaQueryWrapper<TenantUserDO> queryWrapper = new LambdaQueryWrapper<>(TenantUserDO.class);
        queryWrapper.orderByDesc(BaseDO::getCreateTime)
                .eq(null != query.getPublishStatus(), TenantUserDO::getPublishStatus, query.getPublishStatus())
                .eq(null != query.getSex(), TenantUserDO::getSex, query.getSex())
                .eq(null != query.getJob(), TenantUserDO::getJob, query.getJob())
                .eq(StringUtils.isNotBlank(query.getWxUserId()), TenantUserDO::getWxUserId, query.getWxUserId())
                .eq(StringUtils.isNotBlank(query.getDingUserId()), TenantUserDO::getDingUserId, query.getDingUserId())
                .like(StringUtils.isNotBlank(query.getName()), TenantUserDO::getName, query.getName())
                .like(StringUtils.isNotBlank(query.getMobile()), TenantUserDO::getMobile, query.getMobile())
                .like(StringUtils.isNotBlank(query.getTel()), TenantUserDO::getTel, query.getTel())
                .like(StringUtils.isNotBlank(query.getEmail()), TenantUserDO::getEmail, query.getEmail())
        ;

        Page<TenantUserDO> page = cpUserMapper.selectPage(new Page<>(query.getPageNum(), query.getPageSize()), queryWrapper);
        Page<TenantUserVO> result = BeanUtil.convertPage(page, TenantUserVO.class);
        Optional.ofNullable(result.getRecords()).ifPresent(list -> {
            list.stream().forEach(userPageVO -> {
                userPageVO.setSexName(MemberSexEnum.getDescByCode(userPageVO.getSex()));
                Optional.ofNullable(userPageVO.getImage()).ifPresent(image -> {
                    userPageVO.setImage(fileOssProp.getHost().concat(userPageVO.getImage()));
                });
            });
        });
        return result;
    }

    @Override
    public String queryTenantLogo() {
        ResponseResult<ProjectDTO> responseResult = systemProjectClient.queryProjectInfo();
        if (null == responseResult || null == responseResult.getData() || StringUtils.isBlank(responseResult.getData().getLogo())) {
            return "";
        }
        return responseResult.getData().getLogo();
    }

    @Override
    public TenantUserDO selectOneByQueryWrapper(LambdaQueryWrapper<TenantUserDO> queryWrapper) {
        if (queryWrapper == null) {
            return null;
        }
        TenantUserDO entityDO = cpUserMapper.selectOne(queryWrapper);
        return entityDO;
    }

    @Override
    public Boolean addTenantUser(Integer tenantId, TenantUserParam param) {
        //查询商户端是否已经存在当前手机号
        if(StringUtils.isEmpty(param.getMobile())) throw new BusinessException("新添用户手机号不能为空");
        List<Integer> allTenantIds = getTenantIds();
        List<ShopUserDO> shopUserDos = shopUserMapper.selectByMobile(allTenantIds, param.getMobile());
        if (!Collections3.isEmpty(shopUserDos)) {
            throw new BusinessException("当前手机号已是商户员工，请删除后再添加到租户");
        }
        TenantUserDO tenantUserDO = BeanUtil.convertBean(param, TenantUserDO.class);
        tenantUserDO.setUserCode(param.getMobile());
        tenantUserDO.setCreateBy(UserUtils.getUserId());
        tenantUserDO.setCreateTime(LocalDateTime.now());
        tenantUserDO.setModifyTime(LocalDateTime.now());
        tenantUserDO.setPublishStatus(PublishStatusEnum.ENABLE.getValue());
        tenantUserDO.setJob(PublishStatusEnum.ENABLE.getValue());
        //查询所有租户是否存在当前手机号用户

        List<TenantUserPO> tenantUserPos = tenantUserMapper.selectAllByMobile(allTenantIds, param.getMobile());
        if (Collections3.isEmpty(tenantUserPos)) {
            //新增用户
            tenantUserPos = Collections3.emptyList();
            tenantUserDO.setId(GenerateId.generateId());
            tenantUserDO.setPassword(passwordEncoder.encode(userConfigProp.getInitialPwd()));
            tenantUserDO.setImage(StringUtils.isBlank(param.getImage()) ? DefaultPathConstant.DEFAULT_USER_HEAD_IMAGE : param.getImage());
        } else {
            tenantUserDO.setId(tenantUserPos.get(0).getId());
            tenantUserDO.setPassword(tenantUserPos.get(0).getPassword());
            tenantUserDO.setImage(tenantUserPos.get(0).getImage());
        }
        //查询是否存在
        List<Integer> tenantIds = tenantUserPos.stream().map(TenantUserPO::getTenantId).collect(Collectors.toList());
        if (tenantIds.contains(tenantId)) {
            throw new BusinessException("当前手机号已存在");
        }
        return cpUserMapper.insert(tenantUserDO) > 0;
    }

    @Override
    public Boolean modifySelf(TenantUserParam param) {
        TenantUserDO userDO = BeanUtil.convertBean(param, TenantUserDO.class);
        userDO.setUserCode(null);
        userDO.setMobile(null);
        userDO.setPassword(null);
        userDO.setModifyBy(UserUtils.getUserId());
        userDO.setModifyTime(LocalDateTime.now());
        return cpUserMapper.updateById(userDO) > 0;
    }

    @Override
    public TenantUserVO view(Long id) {
        TenantUserBO userBO = getBean().queryById(id);
        return BeanUtil.convertBean(userBO, TenantUserVO.class);
    }

    @Override
    public TenantUserBO queryById(Long id) {
        TenantUserDO userDO = cpUserMapper.selectById(id);
        //userDO.setImage(UrlUtils.addUrlDomainName(fileOssProp.getHost(), userDO.getImage()));
        return BeanUtil.convertBean(userDO, TenantUserBO.class);
    }

    @Override
    public Boolean clearWxUserId(List<Long> ids) {
        if (Collections3.isEmpty(ids)) {
            return true;
        }
        LambdaUpdateWrapper<TenantUserDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(TenantUserDO::getWxUserId, "");
        updateWrapper.in(BaseDO::getId, ids);
        cpUserMapper.update(null, updateWrapper);
        return true;
    }

    @Override
    public Boolean clearWxUserIdByWxUserId(String wxUserId) {
        if (StringUtils.isBlank(wxUserId)) {
            return true;
        }
        LambdaUpdateWrapper<TenantUserDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(TenantUserDO::getWxUserId, "");
        updateWrapper.eq(TenantUserDO::getWxUserId, wxUserId);
        cpUserMapper.update(null, updateWrapper);
        return true;
    }

    @Override
    public List<TenantUserBO> queryLoginCpUserByMobileAndWxUserId(String wxUserId, String mobile) {
        List<TenantUserDO> memberInfoDos = cpUserMapper.selectList(new LambdaQueryWrapper<>(TenantUserDO.class)
                .orderByAsc(BaseDO::getCreateTime)
                .eq(StringUtils.isNotBlank(wxUserId), TenantUserDO::getWxUserId, wxUserId)
                .eq(StringUtils.isNotBlank(mobile), TenantUserDO::getUserCode, mobile)
        );
        return JsonUtils.jsonToList(memberInfoDos, TenantUserBO.class);
    }

    @Override
    public TenantUserBO queryByWxUserId(String wxUserId) {
        TenantUserDO userDO = cpUserMapper.selectOne(new LambdaQueryWrapper<TenantUserDO>().eq(TenantUserDO::getWxUserId, wxUserId));
        //userDO.setImage(UrlUtils.addUrlDomainName(fileOssProp.getHost(), userDO.getImage()));
        return BeanUtil.convertBean(userDO, TenantUserBO.class);
    }

    @Override
    public TenantUserBO queryByDingUserId(String dingUserId) {
        TenantUserDO userDO = cpUserMapper.selectOne(new LambdaQueryWrapper<TenantUserDO>().eq(TenantUserDO::getDingUserId, dingUserId));
        //userDO.setImage(UrlUtils.addUrlDomainName(fileOssProp.getHost(), userDO.getImage()));
        return BeanUtil.convertBean(userDO, TenantUserBO.class);
    }

    @Override
    public TenantUserBO queryByUserCode(String userCode) {
        TenantUserDO userDO = cpUserMapper.selectOne(new LambdaQueryWrapper<TenantUserDO>().eq(TenantUserDO::getUserCode, userCode));
        String domainUrl = fileOssProp.getHost();
        //userDO.setImage(UrlUtils.addUrlDomainName(domainUrl, userDO.getImage()));
        TenantUserBO entityBO=  BeanUtil.convertBean(userDO, TenantUserBO.class);
        return entityBO;
    }

    @Override
    public TenantUserBO queryByMobile(String mobile) {
        TenantUserDO userDO = cpUserMapper.selectOne(new LambdaQueryWrapper<TenantUserDO>().eq(TenantUserDO::getMobile, mobile));
        //userDO.setImage(UrlUtils.addUrlDomainName(fileOssProp.getHost(), userDO.getImage()));
        return BeanUtil.convertBean(userDO, TenantUserBO.class);
    }

    @Override
    public TenantUserBO queryByUserCodeOrMobile(String mobile) {
        TenantUserDO userDO = cpUserMapper.selectOne(new LambdaQueryWrapper<TenantUserDO>()
                .eq(TenantUserDO::getUserCode, mobile).or().eq(TenantUserDO::getMobile, mobile));
        //userDO.setImage(UrlUtils.addUrlDomainName(fileOssProp.getHost(), userDO.getImage()));
        return BeanUtil.convertBean(userDO, TenantUserBO.class);
    }

    @Override
    public Boolean modifyPwd(Long userId, UserModifyPwdParam userModifyPwdParam) {
        TenantUserBO userBO = getBean().queryById(userId);
        String oldPwd;
        String newPwd;
        try {
            oldPwd = EncryptUtils.RsaEncrypt.decrypt(userModifyPwdParam.getOldPassword(), rsaConfig.getKeyPair().getPrivate());
            newPwd = EncryptUtils.RsaEncrypt.decrypt(userModifyPwdParam.getNewPassword(), rsaConfig.getKeyPair().getPrivate());
        } catch (Exception e) {
            throw new EncryptException("密码非用户输入密码");
        }
        if (!passwordEncoder.matches(oldPwd, userBO.getPassword())) {
            throw new BusinessException("旧密码错误");
        }
        return getBean().modifyPassword(userId, newPwd);
    }

    @Override
    public Boolean modifyPassword(Long userId, String newPassword) {
        TenantUserDO userDO = new TenantUserDO();
        userDO.setId(userId);
        userDO.setModifyTime(LocalDateTime.now());
        userDO.setPassword(StringUtils.isEmpty(newPassword) ? passwordEncoder.encode(userConfigProp.getInitialPwd()) : passwordEncoder.encode(newPassword));
        return cpUserMapper.updateById(userDO) > 0;
    }

    @Override
    public Boolean findPwd(UserFindPwdParam userFindPwdParam) {
        TenantUserDO userDO = cpUserMapper.selectOne(new LambdaQueryWrapper<>(TenantUserDO.class)
                .eq(TenantUserDO::getPublishStatus, PublishStatusEnum.ENABLE.getValue())
                .eq(TenantUserDO::getJob, PublishStatusEnum.ENABLE.getValue())
                .eq(TenantUserDO::getMobile, userFindPwdParam.getPhone()));
        if (null == userDO) {
            throw new BusinessException("用户不存在");
        }
        ResponseResult<Boolean> responseResult = msgClient.checkVerificationCode(userFindPwdParam.getKey(),userFindPwdParam.getPhone(), userFindPwdParam.getVerificationCode());
        if (null == responseResult || !responseResult.getData()) {
            throw new BusinessException("验证码错误或已过期");
        }
        String newPwd;
        try {
            newPwd = EncryptUtils.RsaEncrypt.decrypt(userFindPwdParam.getNewPassword(), rsaConfig.getKeyPair().getPrivate());
        } catch (Exception e) {
            throw new EncryptException("密码非用户输入密码");
        }
        return getBean().modifyPassword(userDO.getId(), newPwd);
    }

    @Override
    public LoginUserDTO queryAccountLogin(AccountLoginQuery query) {
        //查询所有租户id
        ResponseResult<List<Integer>> responseResult = globalClient.queryTenantIds();
        if (null == responseResult || Collections3.isEmpty(responseResult.getData())) {
            return null;
        }
        TenantUserPO cpUserPO = cpUserMapper.queryAccountLogin(responseResult.getData(), query.getMobile());
        if (null == cpUserPO) {
            return null;
        }
        LoginUserDTO result = BeanUtil.convertBean(cpUserPO, LoginUserDTO.class);
        Integer tenantId = cpUserPO.getTenantId();
        if(tenantId!=null) {
            TenantDTO tenantDTO = queryTenantById(tenantId);
            if(tenantDTO != null){
                result.setIndustry(tenantDTO.getIndustry());
            }
        }
        result.setLock(!PublishStatusEnum.ENABLE.getValue().equals(cpUserPO.getPublishStatus()));
        result.setJob(PublishStatusEnum.ENABLE.getValue().equals(cpUserPO.getJob()));
        result.setLoginRole(LoginRoleEnum.WX_CP.name());
        return result;
    }

    @Override
    public UserInfoDTO queryLoginUser(HttpServletRequest request, Long userId) {
        TenantUserBO userBO = getBean().queryById(userId);
        if (null == userBO) {
            return null;
        }
        Integer tenantId = Integer.valueOf(request.getHeader(HeaderNounConstant.TENANT_ID));
        UserInfoDTO userInfoDTO = BeanUtil.convertBean(userBO, UserInfoDTO.class);
        userInfoDTO.setTenantId(tenantId);
        if(tenantId!=null) {
            TenantDTO tenantDTO = queryTenantById(tenantId);
            if(tenantDTO != null){
                userInfoDTO.setIndustry(tenantDTO.getIndustry());
                userInfoDTO.setTenantId(Integer.valueOf(tenantId));
            }
        }
        return userInfoDTO;
    }

    @Override
    public Boolean modifyMobile(Long userId, ModifyMobileParam param) {
        ResponseResult<Boolean> responseResult = msgClient.checkVerificationCode(param.getKey(),param.getMobile(), param.getVerificationCode());
        if (null == responseResult || null == responseResult.getData() || !responseResult.getData()) {
            throw new BusinessException("验证码错误或已过期");
        }
        TenantUserDO userDO = new TenantUserDO();
        userDO.setId(userId);
        userDO.setMobile(param.getMobile());
        userDO.setModifyTime(LocalDateTime.now());
        return cpUserMapper.updateById(userDO) > 0;
    }

    @Override
    public Boolean modifyWxUserId(String wxUserId, Long cpUserId) {
        TenantUserDO entityDO = new TenantUserDO();
        entityDO.setId(cpUserId);
        entityDO.setWxUserId(wxUserId);
        entityDO.setModifyTime(LocalDateTime.now());
        cpUserMapper.updateById(entityDO);
        return true;
    }

    @Override
    public CurrentCpUserVO transferCurrentUser(CurrentUser currentUser) {
        CurrentCpUserVO currentUserVO = BeanUtil.convertBean(currentUser, CurrentCpUserVO.class);
        currentUserVO.setSexName(SexEnum.getDescByCode(currentUserVO.getSex()));
        if(StringUtils.isNotEmpty(currentUser.getImage())){
            currentUserVO.setImage(UrlUtils.addUrlDomainName(fileOssProp.getHost(), currentUserVO.getImage()));
        }
        //查询所属租户logo
        currentUserVO.setTenantLogo(getBean().queryTenantLogo());
        return currentUserVO;
    }
    private void checkUnique(String mobile, Long id) {
        //查询所有租户id
        ResponseResult<List<Integer>> responseResult = globalClient.queryTenantIds();
        if (null == responseResult || Collections3.isEmpty(responseResult.getData())) {
            throw new BusinessException("请检查是否有租户");
        }
        //手机号邮箱全局校验
        List<Long> ids = cpUserMapper.checkUnique(responseResult.getData(), mobile);
        if (!Collections3.isEmpty(ids)) {
            if (null == id) {
                throw new BusinessException("请检查手机号是否重复");
            } else {
                if (1 != ids.size() || !ids.contains(id)) {
                    throw new BusinessException("请检查手机号是否重复");
                }
            }
        }

    }

    private List<Integer> getTenantIds() {
        ResponseResult<List<Integer>> responseResult = globalClient.queryTenantIds();
        if (null == responseResult || Collections3.isEmpty(responseResult.getData())) {
            throw new BusinessException("租户不存在");
        }
        return responseResult.getData();
    }

    private TenantDTO queryTenantById(Integer tenantId) {
        ResponseResult<TenantDTO> responseResult = globalClient.queryTenantById(tenantId);
        if (null == responseResult || null == responseResult.getData()) {
            throw new BusinessException("租户不存在");
        }
        return responseResult.getData();
    }

    private ICpUserService getBean() {
        return SpringBeanUtil.getBean(ICpUserService.class);
    }
}
